package com.springblog.config;

import com.springblog.oauth2.OAuth2Filter;
import com.springblog.oauth2.OAuth2Realm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

/**
 * 执行顺序
 * shiroConfig.shiroFilter()  -> preHandle() -> isAccessAllowed()  --false--> onAccessDenied()
 * --> executeLogin() --> Realm.doGetAuthenticationInfo()
 * 1. isAccessAllowed()返回 true, 允许访问，无操作
 * 2. isAccessAllowed()返回 false, 拒绝访问，执行回调方法 onAccessDenied()
 *
 * 登录时必须执行生成 JwtToken 方法，即我自定义的 jwtUtils.generateToken(1L); (执行即可，会自动将 token 保存在 JWT 类中)
 *
 * 3. 由于只有登录等特定接口不用携带Token,因此执行到 onAccessDenied中时，非login接口一定会携带token，执行 executeLogin()
 * 4. 此时执行 ShiroConfig.securityManager()中自定义的校验实现类 Realm,在这也就是传入的 OAuth2Realm
 * 5. OAuth2Realm 必须实现2个方法，一个控制权限，一个控制校验token。因此只需要对 doGetAuthenticationInfo() 进行定义即可
 * 6. doGetAuthenticationInfo 返回info,查看源码可知3参数的意义
 * 第一个参数 principal： 重要信息，此处使用 user实例 作为重要信息
 * 第二个参数 credentials： 认证信息，此处使用 token 作为认证信息  (注意：登陆时已经保存过token，此处是将保存在JWT中的token取出)
 * 第三个参数 realmName： Reaml的类名，此处使用 getName() 方法获取，或者直接写死为字符串 "OAuth2Realm"亦可。
 *
 */

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public SecurityManager securityManager(OAuth2Realm oAuth2Realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth2Realm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new OAuth2Filter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new HashMap<>();

        // anon: 所有路径都通过注解进行管理。因此通过注解管理的接口，无序进行认证
        filterMap.put("/app/**", "anon");
        filterMap.put("/login", "anon");
        filterMap.put("/blog/**", "anon");
        filterMap.put("/device/**", "anon");

        // oauth2: 所有路径都通过 oauth2 进行 token 认证管理
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }
}

